/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 * 
 * This file is part of Indigo toolkit.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

#include "base_cpp/array.h"
#include "base_cpp/output.h"
#include "graph/graph_highlighting.h"
#include "molecule/molecule.h"
#include "reaction/reaction.h"
#include "layout/metalayout.h"
#include "layout/reaction_layout.h"
#include "layout/molecule_layout.h"
#include "molecule/molecule_arom.h"
#include "molecule/molecule_dearom.h"
#include "molecule/molecule_auto_loader.h"
#include "molecule/molfile_loader.h"
#include "molecule/smiles_loader.h"
#include "molecule/molfile_saver.h"
#include "reaction/reaction_auto_loader.h"
#include "reaction/rxnfile_loader.h"
#include "reaction/rsmiles_loader.h"
#include "reaction/rxnfile_saver.h"

#include "render_context.h"
#include "render_molecule.h"
#include "render_reaction.h"
#include "render_params.h"

void RenderParamInterface::loadMol (RenderParams& params, Scanner& scanner)
{
   if (params.inputFormat == INPUT_FORMAT_MOLFILE)
   {
      MolfileLoader loader(scanner);

      if (params.loadHighlighting)
         loader.highlighting = &params.molhl;
      loader.ignore_stereocenter_errors = true;
      loader.loadMolecule(params.mol, params.query);
   }
   else if (params.inputFormat == INPUT_FORMAT_SMILES)
   {
      SmilesLoader loader(scanner);

      if (params.loadHighlighting)
         loader.highlighting = &params.molhl;
      loader.loadMolecule(params.mol, params.query);
      if (!params.query)
         params.mol.calcExplicitValencesByImplicitHydrogens();
   }
   else // INPUT_FORMAT_UNKNOWN
   {
      MoleculeAutoLoader loader(scanner);

      if (params.loadHighlighting)
         loader.highlighting = &params.molhl;
      loader.ignore_stereocenter_errors = true;
      loader.loadMolecule(params.mol, params.query);
   }
   params.mol.calcImplicitHydrogens(true);
   params.rmode = RENDER_MOL;
}

void RenderParamInterface::loadRxn (RenderParams& params, Scanner& scanner)
{
   if (params.inputFormat == INPUT_FORMAT_RXNFILE)
   {
      RxnfileLoader loader(scanner);

      if (params.loadHighlighting)
         loader.highlighting = &params.rhl;

      loader.ignore_stereocenter_errors = true;
      loader.loadReaction(params.rxn, params.query);
   }
   else if (params.inputFormat == INPUT_FORMAT_REACTION_SMILES)
   {
      RSmilesLoader loader(scanner);

      if (params.loadHighlighting)
         loader.highlighting = &params.rhl;

      loader.loadReaction(params.rxn, params.query);

      if (!params.query)
         for (int i = params.rxn.begin(); i != params.rxn.end();
                  i = params.rxn.next(i))
            params.rxn.getMolecule(i).calcExplicitValencesByImplicitHydrogens();
   }
   else // INPUT_FORMAT_UNKNOWN
   {
      ReactionAutoLoader loader(scanner);
      if (params.loadHighlighting)
         loader.highlighting = &params.rhl;
      loader.ignore_stereocenter_errors = true;
      loader.loadReaction(params.rxn, params.query);
   }
   
   params.rxn.calcImplicitHydrogens(true);
   params.rmode = RENDER_RXN;
}

bool RenderParamInterface::needsLayout (const Molecule& mol)
{
   for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i))
      for (int j = mol.vertexNext(i); j < mol.vertexEnd(); j = mol.vertexNext(j))
      {
         const Vec3f& v = mol.getAtomPos(i);
         const Vec3f& w = mol.getAtomPos(j);
         Vec3f d;
         d.diff(v, w);
         d.z = 0;
         if (d.length() < 1e-3)
            return true;
      }
   return false;
}

void RenderParamInterface::render (RenderParams& params)
{
   if (params.rmode == RENDER_NONE)
      throw Error("No object to render specified");

   RenderContext rc;
   rc.setScaleFactor(params.relativeThickness);
   rc.setHighlightingOptions(&params.hlOpt);
   rc.setRenderContextOptions(&params.rcOpt);

   rc.setOutput(params.output);
   rc.setMode(params.mode);
   rc.setHDC(params.hdc);
   rc.setBackground(params.backgroundColor);
   rc.setBaseColor(params.baseColor);

   if (params.query)
      params.rOpt.implHMode = IHM_NONE;

   if (params.rmode == RENDER_MOL) {
      MoleculeRender render(rc);

      render.opt.copy(params.rOpt);
      render.cnvOpt = params.cnvOpt;
      if (needsLayout(params.mol))
      {
         MoleculeLayout ml(params.mol);
         ml.make();
         params.mol.getStereocenters().markBonds();
      }
      if (params.aromatization > 0)
      {
         MoleculeAromatizer::aromatizeBonds(params.mol);
      }
      else if (params.aromatization < 0)
      {
         MoleculeDearomatizer::dearomatizeMolecule(params.mol);
      }
      render.setMolecule(&params.mol);
      render.setMoleculeHighlighting(&params.molhl);

      render.draw();
   } else if (params.rmode == RENDER_RXN) {
      ReactionRender render(rc);

      render.opt.copy(params.rOpt);
      render.cnvOpt = params.cnvOpt;
      for (int i = params.rxn.begin(); i < params.rxn.end(); i = params.rxn.next(i))
      {
         Molecule& mol = params.rxn.getMolecule(i);
         if (needsLayout(mol))
         {
            MoleculeLayout ml(mol);
            ml.make();
            mol.getStereocenters().markBonds();
         }
      }

      if (params.aromatization > 0)
      {
         params.rxn.aromatizeBonds();
      }
      else if (params.aromatization < 0)
      {
         params.rxn.dearomatizeBonds();
      }

      render.setReactionHighlighting(&params.rhl);
      render.setReaction(&params.rxn);
      render.draw();
   } else {
      throw Error("Invalid rendering mode: %i", params.rmode);
   }
   rc.closeContext();
}

void RenderParamInterface::layout(RenderParams& params)
{
   if (params.mode == MODE_LAYOUT_MOL)
   {
      if (params.rmode != RENDER_MOL)
         throw Error("No molecule loaded");
      MoleculeLayout ml(params.mol);
      ml.bond_length = 1.6f;
      ml.make();
      params.mol.getStereocenters().markBonds();

      if (params.aromatization > 0)
      {
         MoleculeAromatizer::aromatizeBonds(params.mol);
      }
      else if (params.aromatization < 0)
      {
         MoleculeDearomatizer::dearomatizeMolecule(params.mol);
      }

      MolfileSaver saver(*params.output);
      if (params.query)
         saver.saveQueryMolecule(params.mol);
      else
         saver.saveMolecule(params.mol);
   } else if (params.mode == MODE_LAYOUT_RXN) {
      if (params.rmode != RENDER_RXN)
         throw Error("No reaction loaded");
      ReactionLayout rl(params.rxn);
      rl.bond_length = 1.6f;
      rl.make();
      params.rxn.markStereocenterBonds();

      if (params.aromatization > 0)
      {
         params.rxn.aromatizeBonds();
      }
      else if (params.aromatization < 0)
      {
         params.rxn.dearomatizeBonds();
      }

      RxnfileSaver saver(*params.output);
      if (params.query)
         saver.saveQueryReaction(params.rxn);
      else
         saver.saveReaction(params.rxn);
   } else {
      throw Error("Incorrect mode. Please select either \"mol\" or \"rxn\" mode to perform layout");
   }
}